#include <iostream>
#include <assert.h>
#include <intrin.h>
#include "Board.h"
#include "Types.h"

#include "bitboard.h"
//using namespace std;
using std::cout;
using std::endl;


Board::Board()
{
	cout << "Board() constructor called" << endl;
	//pieces = new Piece[64];
	//piecesBB = new Bitboard[14];
	Clear();
}

/*
* Create a new board by copy
*/
Board::Board(Board & b)
{
	cout << "Board(Board & b) constructor called" << endl;
	//pieces = new Piece[64];
	//piecesBB = new Bitboard[14];
	memcpy(pieces,b.pieces,64);
	//for (int i = 0; i < 64; i++)
	//{
	//	pieces[i]= b.pieces[i];
	//}
	emptyBB = b.emptyBB;
	occupiedBB = b.occupiedBB;

	memcpy(piecesBB,b.piecesBB,PIECEBB_SIZE);
	//for (int i = 0; i < 14; i++)
	//	piecesBB[i] = b.piecesBB[i];
}


void Board::Clear()
{
	for (int i = 0; i < 64; i++)
	{
		pieces[i] = NO_PIECE;
	}
	emptyBB = 0xffffffffffffffff;
	occupiedBB = 0;

	for (int i = 0; i < PIECEBB_SIZE; i++)
		piecesBB[i] = 0;
}


void Board::Clear(int square)
{
	pieces[square] = NO_PIECE;
	emptyBB |= ((Bitboard)1 << square);
	occupiedBB &= ~((Bitboard)1 << square);
}


void Board::SetPiece(Piece piece, Square square)
{
	assert((square >= 0) && (square <= 63));
	assert(piece != NO_PIECE);

	pieces[square] = piece;
	piecesBB[(int)piece] |= (Bitboard)1 << square;
	occupiedBB |= (Bitboard)1 << square ;
	emptyBB &= ~((Bitboard)1 << square);
}


Piece Board::GetPiece(Square square)
{
	assert((square >= 0) && (square <= 63)); 
	return pieces[square];
}


void Board::DoMove(Move move)
{
	// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
	assert(from_sq(move) != to_sq(move));

	//this->DebugDisplay();

	Piece piece = pieces[from_sq(move)];
	Piece cPiece = pieces[to_sq(move)];

	//if((cPiece == W_KING) || (cPiece == B_KING)){
	//	cout << "WARNING !!! illegal move (before)" << endl;
	//	this->DebugDisplay();
	//}

	pieces[to_sq(move)] = pieces[from_sq(move)];
	pieces[from_sq(move)] =  NO_PIECE;

	Bitboard fromBB = (Bitboard)1 << from_sq(move);
	Bitboard toBB = (Bitboard)1 << to_sq(move);
	Bitboard fromToBB = fromBB ^ toBB; // |+
	piecesBB[(int)piece] ^= fromToBB;   // update piece bitboard
	//piecesBB[(int)piece%2] ^= fromToBB;   // update white or black color bitboard
	if (cPiece ==  NO_PIECE)
	{
		// quiet move
		occupiedBB ^= fromToBB;   // update occupied ...
		emptyBB ^= fromToBB;   // ... and empty bitboard
	}
	else
	{
		// capture
		piecesBB[(int)cPiece] ^= toBB;       // reset the captured piece
		//piecesBB[(int)cPiece%2] ^= toBB;       // update color bitboard by captured piece
		occupiedBB ^= fromBB;     // update occupied, only from becomes empty
		emptyBB ^= fromBB;     // update empty bitboard
	}



	//cout << "Occupied");
	//BitboardHelper.DebugDisplay(occupiedBB);
	//cout << "empty");
	//BitboardHelper.DebugDisplay(emptyBB);

	//this->DebugDisplay();

	//if((cPiece == W_KING) || (cPiece == B_KING)){
	//	cout << "WARNING !!! illegal move (after)" << endl;
	//	this->DebugDisplay();
	//}

	// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
}

void Board::UndoMove(Move move, Piece piece, Piece cPiece)
{
	//// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
	assert(from_sq(move) != to_sq(move));

	//this->DebugDisplay();

	//Piece piece = pieces[to_sq(move)];

	pieces[from_sq(move)] = piece;
	pieces[to_sq(move)] = cPiece;

	Bitboard fromBB = (Bitboard)1 << from_sq(move);
	Bitboard toBB = (Bitboard)1 << to_sq(move);
	Bitboard fromToBB = fromBB ^ toBB; // |+
	piecesBB[(int)piece] ^= fromToBB;   // update piece bitboard
	//piecesBB[(int)piece%2] ^= fromToBB;   // update white or black color bitboard
	if (cPiece ==  NO_PIECE)
	{
		// quiet move
		occupiedBB ^= fromToBB;   // update occupied ...
		emptyBB ^= fromToBB;   // ... and empty bitboard
	}
	else
	{
		// capture
		piecesBB[(int)cPiece] ^= toBB;       // reset the captured piece
		//piecesBB[(int)cPiece%2] ^= toBB;       // update color bitboard by captured piece
		occupiedBB ^= fromBB;     // update occupied, only from becomes empty
		emptyBB ^= fromBB;     // update empty bitboard
	}

	//
	//cout << "Occupied");
	//BitboardHelper.DebugDisplay(occupiedBB);
	//cout << "empty");
	//BitboardHelper.DebugDisplay(emptyBB);
	//

	//this->DebugDisplay();

	// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
}


bool Board::IsSquareEmpty(Square square)
{
	return (pieces[square] == NO_PIECE);
}



bool Board::IsEqual(Board& b)
{
	// TODO: shorter with bitboards
	for (int i = 0; i < 64; i++) 
		if (pieces[i] != b.pieces[i]) 
			return false;
	return true;
}

//
//override int GetHashCode()
//{
//int code = 0;
//for (int i = 0; i < 64; i++) code += ((int)this->pieces[i])*(13 ^ i);
//return code;
//
////return this->pieces.GetHashCode();
//}
//

int Board::Evaluate()
{
	// TODO: test values. 
	// BE CAREFULL: the coefficients are closely related to the 'Piece' enum
	//const int coeff[] = { 0, 0, 85, -85, 325, -325, 325, -325, 500, -500, 975, -975, 10000000, -10000000 };
	const int coeff[] = { 0, 85, 325, 325, 500, 975, 30000, 0, 0, -85, -325, -325, -500, -975, -30000, 0,0};
//	enum Piece {
//  NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
//  W_PAWN = 1, W_KNIGHT =  2, W_BISHOP =  3, W_ROOK =  4, W_QUEEN =  5, W_KING =  6,
//  B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
//};


	//this->DebugDisplay();
	// classic eval part
	int result = 0;
	Piece p = NO_PIECE; ;
	for (int i = 0; i < 64; i++)
	{
		p = this->pieces[i];
		if (p !=  NO_PIECE)
			result += coeff[(int)p];
	}

	return result;
}

void Board::DebugDisplay()
{
	// Board display
	// Useful to detect an incorrect position
	cout << "BOARD" << endl;
	for (int i = 7; i >= 0; i--)
	{
		cout << " +---+---+---+---+---+---+---+---+" << endl;
		for (int j = 0; j < 8; j++)
			cout << " | " << PieceToChar(pieces[i * 8 + j]);
		cout << " |" << endl;
	}
	cout << " +---+---+---+---+---+---+---+---+" << endl;

	//// bitboards debug display 
	// Useful to detect an incorrect bitboard usage
	//cout << "BITBOARDS" << endl;
	//for(int i=0;i<16;i++) {
	//	// displays only non-empty
	//	if(piecesBB[i]) {
	//		cout << "BITBOARD [" << "-PNBRQK??pnbrqk -"[i] << "]" <<endl;
	//		Bitboards::print(piecesBB[i]);
	//	}
	//}
}

  //NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
  //W_PAWN = 1, W_KNIGHT =  2, W_BISHOP =  3, W_ROOK =  4, W_QUEEN =  5, W_KING =  6,
  //B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
char Board::PieceToChar(Piece p)
{
	const char pchars[] = " PNBRQK  pnbrqk  ";
	return pchars[(int)p];
}

#pragma intrinsic(_BitScanForward) // WARNING: this is not _BitScanForward64
bool Board::IsCheck(Color side)
{
	assert(piecesBB[(int) W_KING] != 0);
	assert(piecesBB[(int) B_KING] != 0);
	//unsigned long square = 0;
	//_BitScanForward(&square, piecesBB[make_piece(side,KING)]);
	//slowBitScanForward(&square, piecesBB[make_piece(side,KING)]);
	Square square = first_1(piecesBB[make_piece(side,KING)]);
	return IsCheck(square, (Color)(1-side));
}



//
//bool attacksToKing(int square, int side)
//{
//BitboardHelper bbh = BitboardHelper.Instance;
//
//Bitboard opPawns = piecesBB[(int) W_PAWN + side];
//Bitboard opKnights = piecesBB[(int) W_KNIGHT + side];
//Bitboard opRQ = piecesBB[(int) W_QUEEN + side];
//Bitboard opBQ = opRQ;
//opRQ |= piecesBB[(int) W_ROOK + side];
//opBQ |= piecesBB[(int) W_BISHOP + side];
//return (bbh.arrPawnAttacks[side,square] & opPawns)
//| (bbh.arrKnightAttacks[square] & opKnights)
//| (bbh.bishopAttacks(occupiedBB, square) & opBQ)
//| (bbh.rookAttacks(occupiedBB, square) & opRQ)) ;
//}

bool Board::IsCheck(Square square, Color bySide)
{
	assert((square >= 0) && (square < 64));
	// TODO: change this workaround directly in the enum 'Piece'

	Bitboard pawns = piecesBB[make_piece(bySide,PAWN)];
	Bitboard knights = piecesBB[make_piece(bySide,KNIGHT)];
	Bitboard king = piecesBB[make_piece(bySide,KING)];

	//if((square == SQ_D8) || (square == SQ_F8)){
	//	this->DebugDisplay();
	//	cout << "StepAttacksBB[make_piece(BLACK-bySide, PAWN)][square]" << endl;
	//	Bitboards::print(StepAttacksBB[make_piece(BLACK-bySide, PAWN)][square]);
	//	cout << "pawns" << endl;
	//	Bitboards::print(pawns);
	//}

	if (StepAttacksBB[make_piece(BLACK-bySide, PAWN)][square] & pawns) 
		return true;
	if (StepAttacksBB[make_piece(bySide, KNIGHT)][square] & knights) 
		return true;
	if (StepAttacksBB[make_piece(bySide, KING)][square] & king) 
		return true;

	Bitboard bishopsQueens = piecesBB[make_piece(bySide, QUEEN)]
	| piecesBB[make_piece(bySide, BISHOP)];
	Bitboard bishopAttacks = attacks_bb<BISHOP>(square, occupiedBB);
	if (bishopAttacks & bishopsQueens)
	{
		//
		//this->DebugDisplay();
		//BitboardHelper.DebugDisplay(occupiedBB,"occupiedBB");
		//BitboardHelper.DebugDisplay(emptyBB, "emptyBB");
		//BitboardHelper.DebugDisplay((bbh.bishopAttacks(occupiedBB, square)),"bishopAttacks");
		//BitboardHelper.DebugDisplay((bbh.bishopAttacks(occupiedBB, square) & bishopsQueens), "bishopAttacks & bishopsQueens");
		//

		return true;
	}
	Bitboard rooksQueens = piecesBB[make_piece(bySide, QUEEN)]
	| piecesBB[make_piece(bySide, ROOK)];
	Bitboard rookAttacks = attacks_bb<ROOK>((Square)square, occupiedBB);
	if (rookAttacks & rooksQueens)
	{
		
		////this->DebugDisplay();
		//cout << "occupiedBB" << endl;
		//Bitboards::print(occupiedBB);
		//cout << "emptyBB" << endl;
		//Bitboards::print(emptyBB);
		//cout << "rookAttacks" << endl;
		//Bitboards::print(rookAttacks);
		//cout << "rooksQueens" << endl;
		//Bitboards::print(rooksQueens);
		//cout << "rookAttacks & rooksQueens" << endl;
		//Bitboards::print(rookAttacks & rooksQueens);
		

		return true;
	}
	return false;
}

